package krao.data;

/**
 * Class for storing data for one role atom of a query, e.g. P(x,y), Q(_,x) or
 * Parent(John, Mary).
 * 
 * @author Pepe
 * 
 */
public class RoleAtom extends Atom {
	String firstArgument;
	String secondArgument;

	public RoleAtom(String name, String arg1, String arg2) {
		super(name);
		firstArgument = getArg(arg1);
		secondArgument = getArg(arg2);
	}

	@Override
	public String toString() {
		return String.format("%s(%s,%s)", getName(), firstArgument == null ? "_"
				: firstArgument, secondArgument == null ? "_" : secondArgument);
	}

	@Override
	public RoleAtom clone() {
		return new RoleAtom(getName(), firstArgument, secondArgument);
	}

	@Override
	public String getFirstArg() {
		return firstArgument;
	};

	@Override
	public boolean getFirstArgVariable() {
		return firstArgument == null ? false : (firstArgument.length() == 1 ? true
				: false);
	};

	@Override
	public String getSecondArg() {
		return secondArgument;
	};

	@Override
	public boolean getSecondArgVariable() {
		return secondArgument == null ? false : (secondArgument.length() == 1 ? true
				: false);
	};

	@Override
	public void setFirstArg(String arg) {
		firstArgument = arg;
	};

	@Override
	public void setSecondArg(String arg) {
		secondArgument = arg;
	};

	@Override
	public boolean isClassAtom() {
		return false;
	}

	@Override
	/**
	 * The atom is empty, if it has no non-null arguments.
	 */
	public boolean isEmpty() {
		return getFirstArg() == null && getSecondArg() == null;
	}

	@Override
	/**
	 * For an atom to be equal to this atom, the second atom must be role atom, 
	 * their names must be equal and their arguments must be equal 
	 * (either both null, or String.equal()).
	 */
	public boolean equals(Object other) {
		if (other == null) {
			return false;
		}
		Atom atom = (Atom) other;
		return !atom.isClassAtom()
				&& this.getName().equals(atom.getName())
				&& ((this.getFirstArg() == null && atom.getFirstArg() == null) || (this
						.getFirstArg() != null && this.getFirstArg().equals(
						atom.getFirstArg())))
				&& ((this.getSecondArg() == null && atom.getSecondArg() == null) || (this
						.getSecondArg() != null && this.getSecondArg().equals(
						atom.getSecondArg())));
	}

	/**
	 * Unification means merging two similar atoms. Ex.:
	 * <ul>
	 * <li>P(_, x) vs P(y, x) = P(y, x)</li>
	 * <li>P(_, _) vs P(x, _) = P(x, _)</li>
	 * </ul>
	 * 
	 * @return the result of unification. If these two atoms don't unify (either
	 *         because of different names, different types, or incompatible
	 *         arguments), null is returned.
	 */
	@Override
	public Atom unify(Atom atom) {
		if (atom == null) {
			return null;
		}

		if (!atom.isClassAtom() && this.getName().equals(atom.getName())) {
			String thisArg1 = this.getFirstArg();
			String thisArg2 = this.getSecondArg();
			String atomArg1 = atom.getFirstArg();
			String atomArg2 = atom.getSecondArg();

			if (thisArg1 == null) {
				if (atomArg1 != null) {
					// P(_, .) vs P(x, .) = P(x, .)
					this.setFirstArg(atomArg1);
				}
			} else {
				if (atomArg1 != null) {
					// P(x, .) vs P(?, .) = P(x==?, .) || null
					if (!thisArg1.equals(atomArg1)) {
						// P(x, .) vs P(y, .) = don't unify -> null
						return null;
					}
				}
			}

			if (thisArg2 == null) {
				if (atomArg2 != null) {
					// P(., _) vs P(., x) = P(., x)
					this.setSecondArg(atomArg2);
				}
			} else {
				if (atomArg2 != null) {
					// P(., x) vs P(., ?) = P(x==?, .) || null
					if (!thisArg2.equals(atomArg1)) {
						// P(., x) vs P(., y) = don't unify -> null
						return null;
					}
				}
			}
			return this;
		}
		return null;
	}

	/**
	 * String used in determining unbound variables. Only variable parameters
	 * are taken into account.
	 * 
	 * @return string in format
	 */
	@Override
	public String getArgString() {
		String first = "";
		String second = "";
		if (getFirstArg() != null && getFirstArgVariable()) {
			first = getFirstArg();
		}
		if (getSecondArg() != null && getSecondArgVariable()) {
			second = getSecondArg();
		}
		return first + second;
	}

	@Override
	public int getArgPosition(String argName) {
		if (argName == null) {
			return 0;
		}
		if (argName.equals(this.getFirstArg())) {
			return 1;
		}
		if (argName.equals(this.getSecondArg())) {
			return 2;
		}
		return 0;
	}

	/**
	 * Compares this atom to another atom.
	 * 
	 * Sort order: names, first arg, second arg.<br/>
	 * Names are compared as strings.<br/>
	 * Arguments are sorted this way:
	 * <ul>
	 * <li>null</li>
	 * <li>variable (char value)</li>
	 * <li>object (string value)</li>
	 * </ul>
	 * ... MEH!
	 * 
	 * @Override public int compareTo(Atom other) { // if names are different,
	 *           compare by them if (!this.getName().equals(other.getName())) {
	 *           return this.getName().compareTo(other.getName()); } // equal
	 *           names, sort by args int thisScore = 0; int otherScore = 0; if
	 *           (this.getFirstArg() == null) { thisScore = 1; } if
	 *           (this.getFirstArgVariable()) { thisScore = 2; } else {
	 *           thisScore = 3; } if (other.getFirstArg() == null) { thisScore =
	 *           1; } if (other.getFirstArgVariable()) { thisScore = 2; } else {
	 *           thisScore = 3; } // if this is smaller than other if (thisScore
	 *           < otherScore) { return -1; } else if (thisScore > otherScore) {
	 *           return 1; } else }
	 */

	public String getArgAtPosition(int otherPosition) {
		if (otherPosition == 1) {
			return getFirstArg();
		}
		return getSecondArg();

	}
}
